package com.phoenix.jsef.common;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;


/**
 * 反射工具类
 * 
 * @author 李启联
 *
 */
public class ReflectionUtils {
	/**
	 * 将反射时的 "检查异常" 转换为 "运行时异常"
	 * @return
	 */
	public static IllegalArgumentException convertToUncheckedException(Exception ex){
		if(ex instanceof IllegalAccessException || ex instanceof IllegalArgumentException
				|| ex instanceof NoSuchMethodException){
			throw new IllegalArgumentException("反射异常", ex);
		}else{
			throw new IllegalArgumentException(ex);
		}
	}
	
	/**
	 * 转换字符串类型到 toType 类型, 或 toType 转为字符串
	 * @param value:  待转换的字符串
	 * @param toType: 提供类型信息的 Class, 可以是基本数据类型的包装类或指定格式日期型
	 * @return
	 */
	public static Object convertValue(Object value, Class<?> toType){
		try {
			DateConverter dc = new DateConverter();
			
			dc.setUseLocaleFormat(true);
			dc.setPatterns(new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"});

			ConvertUtils.register(dc, Date.class);
			
			return ConvertUtils.convert(value, toType);
		} catch (Exception e) {
			e.printStackTrace();
			throw convertToUncheckedException(e);
		}
	}

	/**
	 * 提取集合中的对象的属性(通过 getter 方法), 组成 List
	 * @param collection: 来源集合
	 * @param propertyName: 要提取的属性名
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List fetchElementPropertyToList(Collection collection, String propertyName){
		List list = new ArrayList();
		
		try {
			for(Object obj: collection){
				list.add(PropertyUtils.getProperty(obj, propertyName));
			}
		} catch (Exception e) {
			e.printStackTrace();
			convertToUncheckedException(e);
		}
		
		return list;
	}
	
	/**
	 * 提取集合中的对象属性(通过 getter 函数), 组合成由分隔符分隔的字符串
	 * @param collection: 来源集合
	 * @param propertyName: 要提取的属性名
	 * @param seperator: 分隔符
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static String fetchElementPropertyToString(Collection collection, String propertyName, 
			String seperator){
		List list = fetchElementPropertyToList(collection, propertyName);
		return StringUtils.join(list, seperator);
	}
	
	/**
	 * 通过反射, 获得定义 Class 时声明的父类的泛型参数的类型
	 * 如: public EmployeeDao extends BaseDao<Employee, String>
	 * @param clazz
	 * @param index
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Class getSuperClassGenricType(Class clazz, int index){
		Type genType = clazz.getGenericSuperclass();
		
		if(!(genType instanceof ParameterizedType)){
			return Object.class;
		}
		
		Type [] params = ((ParameterizedType)genType).getActualTypeArguments();
		
		if(index >= params.length || index < 0){
			return Object.class;
		}
		
		if(!(params[index] instanceof Class)){
			return Object.class;
		}
		
		return (Class) params[index];
	}
	
	/**
	 * 通过反射, 获得 Class 定义中声明的父类的泛型参数类型
	 * 如: public EmployeeDao extends BaseDao<Employee, String>
	 * @param <T>
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static<T> Class<T> getSuperGenericType(Class clazz){
		return getSuperClassGenricType(clazz, 0);
	}
	
	/**
	 * 获取field的类型，如果是复合对象，获取的是泛型的类型
	 * 
	 * @param field
	 * @return
	 */
	public static Class getFieldClass(Field field) {
		Class fieldClazz = field.getType();
		//if (fieldClazz.isAssignableFrom(List.class)) {
        if (Collection.class.isAssignableFrom(fieldClazz)) {
			/*
			 * 示例：Class1.isAssignableFrom(Class2) 解释：①
			 * 判断Class1和Class2是否相同。②Class1是否是Class2的父类或者接口。
			 **/
			Type fc = field.getGenericType(); // 关键的地方，如果是List或Set类型，得到其Generic的类型
			if (fc instanceof ParameterizedType) // 如果是泛型参数的类型
			{
				ParameterizedType pt = (ParameterizedType) fc;
				fieldClazz = (Class) pt.getActualTypeArguments()[0]; // 得到泛型里的class类型对象。
			}
		}
		return fieldClazz;
	}
	
	/**
	 * 循环向上转型, 获取对象的 DeclaredMethod
	 * @param object
	 * @param methodName
	 * @param parameterTypes
	 * @return
	 */
	public static Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes){
		
		for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){
			try {
				//superClass.getMethod(methodName, parameterTypes);
				return superClass.getDeclaredMethod(methodName, parameterTypes);
			} catch (NoSuchMethodException e) {
				//Method 不在当前类定义, 继续向上转型
			}
			//..
		}
		
		return null;
	}
	
	/**
	 * 使 filed 变为可访问
	 * @param field
	 */
	public static void makeAccessible(Field field){
		if(!Modifier.isPublic(field.getModifiers())){
			field.setAccessible(true);
		}
	}
	
	/**
	 * 循环向上转型, 获取对象的 DeclaredField
	 * @param object
	 * @param filedName
	 * @return
	 */
	public static Field getDeclaredField(Object object, String filedName){
		
		for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){
			try {
				return superClass.getDeclaredField(filedName);
			} catch (NoSuchFieldException e) {
				//Field 不在当前类定义, 继续向上转型
			}
		}
		return null;
	}
	
	/**
	 * 直接调用对象方法, 而忽略修饰符(private, protected)
	 * @param object
	 * @param methodName
	 * @param parameterTypes
	 * @param parameters
	 * @return
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 */
	public static Object invokeMethod(Object object, String methodName, Class<?> [] parameterTypes,
			Object [] parameters) throws InvocationTargetException{
		
		Method method = getDeclaredMethod(object, methodName, parameterTypes);
		
		if(method == null){
			throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]");
		}
		
		method.setAccessible(true);
		
		try {
			return method.invoke(object, parameters);
		} catch(IllegalAccessException e) {

		} 
		
		return null;
	}
	
	/**
	 * 调用执行对象里指定的方法
	 * @param obj 调用执行的对象
	 * @param methodName 调用执行的对象的指定方法名
	 * @param args 调用执行指定方法所需要的参数
	 * @return
	 */
	public static Object invokeMethod(Object obj,String methodName,Object ... args){
		Class [] parameterTypes = new Class[args.length];
		for(int i=0; i<args.length;i++){
			parameterTypes[i] = args[i].getClass();
		}
		try{
			Method method = getDeclaredMethod(obj.getClass(),methodName,parameterTypes);
		    method.setAccessible(true);
			return method.invoke(obj, args);
		} catch(Exception e){
			e.printStackTrace();
		}
		
		return null;
	}
	
	/**
	 * 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter
	 * @param object
	 * @param fieldName
	 * @param value
	 */
	public static void setFieldValue(Object object, String fieldName, Object value){
		Field field = getDeclaredField(object, fieldName);
		
		if (field == null)
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		
		makeAccessible(field);
		
		try {
			field.set(object, value);
		} catch (IllegalAccessException e) {

		}
	}
	
	/**
	 * 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter
	 * @param object
	 * @param fieldName
	 * @return
	 */
	public static Object getFieldValue(Object object, String fieldName){
		Field field = getDeclaredField(object, fieldName);
		
		if (field == null)
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		
		makeAccessible(field);
		
		Object result = null;
		
		try {
			result = field.get(object);
		} catch (IllegalAccessException e) {

		}
		
		return result;
	}
		
	/**
	 * 根据输入指定的不分大小写的名称在类型中找到实际定义的属性名称
	 * 如：在实体中定义了 public string LongRiver{get;set;} 则用 longriver 或 LONGRIVER 都可返回实际定义的 LongRiver 这属性名称
	 * @param clazz 实体對象类型【其对象的字节码】
	 * @param likeName 所知道属性名称（与大小写无关）
	 * @return 实体對象定义的属性名称
	 */
	public static String getPropertyName(Class<?> clazz, String likeName) {
		String propertyName = "";
		for (Field field : clazz.getDeclaredFields()) {
			makeAccessible(field);
			if (CUtils.underlineToCamel(field.getName()).toUpperCase().equals(CUtils.underlineToCamel(likeName).toUpperCase())) {
				propertyName = field.getName();
				break;
			}		
		}
		return propertyName;
	}
	
	/**
	 * 根据输入指定的不分大小写的名称在类型中找到实际定义的属性名称对应的数据库定义的字段名称
	 * @param clazz 实体對象类型【其对象的字节码】
	 * @param likeName 所知道属性名称（与大小写无关）
	 * @return 实体對象定义的属性对应到数据库中的列名
	 */
    public static String getPropertyNameMatchDbColumnName(Class<?> clazz , String likeName)
    {
    	String dbColumnName = "";
/*        for(Field field : clazz.getDeclaredFields())
        {
			makeAccessible(field);
			if (field.getName().toUpperCase().equals(likeName.toUpperCase())) {
				if (field.isAnnotationPresent(EntityFieldAttribute.class)) {
					EntityFieldAttribute fieldAttribute = (EntityFieldAttribute) field
							.getAnnotation(EntityFieldAttribute.class);
					if (fieldAttribute.columnName() != null)
						dbColumnName = fieldAttribute.columnName();
				} else
					dbColumnName = field.getName();
				break;
			}
        }*/
        return dbColumnName;
    }
	
    /**
     * 判断字段是否static修饰
     * @param field
     * @return
     */
    public static boolean isStaticField(Field field) {
    	return Modifier.isStatic(field.getModifiers());
    }
    
    /**
     * 判断字段是否final修饰
     * @param field
     * @return
     */
    public static boolean isFinalField(Field field) {
    	return Modifier.isFinal(field.getModifiers());
    }
    
    /**
     * 判断字段是否static final 修饰
     * 通常这么修饰的字段是用来定义常量的
     * @param field
     * @return
     */
    public static boolean isStaticFinalField (Field field) {
    	return (isStaticField(field) && isFinalField(field));
    }

}
